package com.binance.basedao.mybatis;

import com.binance.basedao.dialect.AbstractDialect;
import com.binance.basedao.dialect.MySql5Dialect;
import com.binance.basedao.dialect.OracleDialect;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.DefaultReflectorFactory;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.factory.DefaultObjectFactory;
import org.apache.ibatis.reflection.wrapper.DefaultObjectWrapperFactory;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.RowBounds;

import java.sql.Connection;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * <b>Description:</b>〈用于代码中分页的SQL拦截器〉<br/>
 * @author hongchaoMao <br/>
 *         Create date: 2017/9/18
 * @version v1.0.0
 */
@SuppressWarnings("all")
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
public class SqlInterceptor implements Interceptor
{
    
    protected final Logger logger    = Logger.getLogger(getClass().getName());
    private static  ThreadLocal<RowBounds> rowBounds = new ThreadLocal<>();
    
    public static RowBounds getRowBounds()
    {
        RowBounds rowBounds = SqlInterceptor.rowBounds.get();
        SqlInterceptor.rowBounds.remove();
        return rowBounds;
    }
    
    public static void setRowBounds(RowBounds rowBounds)
    {
        SqlInterceptor.rowBounds.set(rowBounds);
    }
    
    @Override
    public Object intercept(Invocation invocation) throws Throwable
    {
        StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
        MetaObject metaStatementHandler = MetaObject.forObject(statementHandler, new DefaultObjectFactory(),
                new DefaultObjectWrapperFactory(), new DefaultReflectorFactory());
        String sql = (String) metaStatementHandler.getValue("delegate.boundSql.sql");
        if (sql.trim().startsWith("UPDATE") || sql.trim().startsWith("INSERT")){
            return invocation.proceed();
        }

        RowBounds rowBounds = getRowBounds();
        if (rowBounds == null) {
            rowBounds = (RowBounds) metaStatementHandler.getValue("delegate.rowBounds");
        }
        Configuration configuration = (Configuration) metaStatementHandler.getValue("delegate.configuration");
        AbstractDialect.Type databaseType = null;
        try {
            databaseType = AbstractDialect.Type.valueOf(configuration.getVariables().getProperty("dialect").toUpperCase());
        }
        catch (Exception e) {
            logger.log(Level.WARNING,"intercept() Database dialect configuration error: ", e);
        }
        if (databaseType == null) {
            throw new RuntimeException("the value of the dialect property in configuration.xml is not defined : " + configuration.getVariables()
                    .getProperty("dialect"));
        }
        AbstractDialect dialect = null;
        switch (databaseType) {
            case MYSQL:
                dialect = new MySql5Dialect();
                break;
            case ORACLE:
                dialect = new OracleDialect();
                break;
        }
        if ((rowBounds != null) && (rowBounds != RowBounds.DEFAULT)) {
            sql = dialect.getLimitString(sql, rowBounds.getOffset(), rowBounds.getLimit());
            metaStatementHandler.setValue("delegate.boundSql.sql", sql);
            metaStatementHandler.setValue("delegate.rowBounds.offset", RowBounds.NO_ROW_OFFSET);
            metaStatementHandler.setValue("delegate.rowBounds.limit", RowBounds.NO_ROW_LIMIT);
        }
        return invocation.proceed();
    }
    
    @Override
    public Object plugin(Object target)
    {
        return Plugin.wrap(target, this);
    }
    
    @Override
    public void setProperties(Properties properties)
    {
    }
    
}
